#include <string>
#include <functional>

#include <unistd.h>             // for system api close() ...
#include <sys/socket.h>         // for socket api like: socket(), bind(), listen(), connect(), send, recv(), setsockopt()...
#include <netinet/in.h>         // for struct sockaddr_in
#include <arpa/inet.h>          // for hton*() inet_addr()
#include <sys/fcntl.h>          // for fcntl() -- NonBlock
#include <string.h>
// log module
//===============
#include <ctime>    // time() localtime() strftime()

enum LOG_LEVEL{
    INFO = 0,
    DBG,
    ERROR,
    FATAL
};

#define CUR_LOG_LEVEL INFO          // 打印日志的等级门槛
#define LOG(LEVEL, FORMAT, ...) do{      \
    if(LEVEL < CUR_LOG_LEVEL) break;\
    time_t time = ::time(nullptr);\
    struct tm *local_tm = ::localtime(&time);\
    char time_str[32] = {0};\
    strftime(time_str, 31, "%Y-%m-%d %H-%M-%S", local_tm);\
    fprintf(stdout, "[%s %s:%d] " FORMAT "\n", \
        time_str, __FILE__, __LINE__, ##__VA_ARGS__);\
}while(0)

#define INFO_LOG(FORMAT, ...) LOG(INFO, FORMAT, ##__VA_ARGS__)
#define DBG_LOG(FORMAT, ...) LOG(DBG, FORMAT, ##__VA_ARGS__)
#define ERROR_LOG(FORMAT, ...) LOG(ERROR, FORMAT, ##__VA_ARGS__)
#define FATAL_LOG(FORMAT, ...) LOG(FATAL, FORMAT, ##__VA_ARGS__)

//===============
class Socket{
private:
    int _sockfd;

private:
    struct sockaddr_in CreateAddr(const std::string &ip, uint16_t port){
        struct sockaddr_in addr;    // <netinet/in.h>
        addr.sin_family = AF_INET;
        addr.sin_port = ::htons(port);
        addr.sin_addr.s_addr = ::inet_addr(ip.c_str());// <arpa/inet.h>
        return addr;
    }
    void NonBlock(){
        int flag = ::fcntl(_sockfd, F_GETFL);
        ::fcntl(_sockfd, F_SETFL, flag | O_NONBLOCK);
    }
    // SOL -> Socket Option Level
    /**
     * @note SO_REUSEADDR allows the socket to bind to an addr that is in the 'TIME_WAIT' state.
     * @note SO_REUSEPORT multiplexing_IO reuseable: 多个进程｜线程绑定监听同一个端口
     * @attention both options are applied at the SOL_SOCKET level
     * @note Required Linux kernel >= 3.9 for SO_REUSEPORT.
     */
    void ReuseAddress(){
        int opt = 1;
        for(int flag : {SO_REUSEADDR, SO_REUSEPORT}){
            ::setsockopt(_sockfd, SOL_SOCKET, flag, (void*)&opt, sizeof(opt));
        }
    }
public:
    Socket(int fd = -1):_sockfd(fd){}
    bool Create(){
        _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if(_sockfd < 0) return false;
        return true;
    }
    void Close(){
        if(_sockfd != -1){
            ::close(_sockfd);
            _sockfd = -1;
        }
    }
    bool Bind(const std::string &ip, uint16_t port){
        struct sockaddr_in addr = CreateAddr(ip, port);
        int ret = ::bind(_sockfd, (sockaddr*)&addr, sizeof(sockaddr_in));   // on success zero is returned. on error, -1 is returned. errno is set to indicate the error
        if(ret < 0){
            ERROR_LOG("bind addr failed");
            return false;
        }
        return true;
    }
    /**
     * @param backlog: default 1024
     */
    bool Listen(int backlog = 1024){
        int newfd = ::listen(_sockfd, backlog);
        if(newfd < 0){
            ERROR_LOG("listen failed");
            return false;
        }
        return true;
    }
    // Cli use to get in touch with Svr
    bool Connect(const std::string &ip, uint16_t port){
        struct sockaddr_in addr = CreateAddr(ip, port);
        int ret = ::connect(_sockfd, (sockaddr*)&addr, sizeof(sockaddr_in));
        if(ret < 0){
            ERROR_LOG("connect failed");
            return false;
        }
        return true;
    }
    /**
     *  int accept(int sockfd, struct sockaddr *_Nullable restrict addr,
                  socklen_t *_Nullable restrict addrlen);
     */
    int Accept(){
        int newfd = ::accept(_sockfd, nullptr, nullptr);
        if(newfd < 0){
            ERROR_LOG("socket accept failed");
            return -1;
        }
        return newfd;
    }
    // Template method
    /**
     * @param ip: default binding to all available network interfaces.
     */
    bool CreateServer(uint16_t port,const std::string &ip = "0.0.0.0", bool non_block = true){
        INFO_LOG("Create Server");
        if(!Create()) return false;
        // ReuseAddress();
        if(non_block) NonBlock();
        if(!Bind(ip, port)) return false;
        if(!Listen()) return false;
        return true;
    }
    bool CreateClient(uint16_t port,const std::string &ip){
        INFO_LOG("Create Client");
        if(!Create()) return false;
        if(!Connect(ip, port)) return false;
        return true;
    }

    ssize_t Send(const void *buf, size_t len, int flag = 0){
        for(;;){
            ssize_t ret = ::send(_sockfd, buf, len, flag);
            if(ret >= 0) return ret;
            if(errno == EINTR) continue;
            else if(errno == EAGAIN || errno == EWOULDBLOCK) return -2;
            else if(errno == EPIPE) {ERROR_LOG("send failed: broken pipe"); return -1;}
            else {ERROR_LOG("send failed: %s", strerror(errno)); return -1;}
            return -1;
        }
    }
    ssize_t NonBlockSend(const void *buf, size_t len){
        if(len < 0) return 0; // robust
        return Send(buf, len, MSG_DONTWAIT);
    }

    ssize_t Recv(void *buf, size_t len, int flag = 0){
        for(;;){
            ssize_t ret = ::recv(_sockfd, buf, len, flag);
            if(ret > 0) return ret;
            if(ret == 0) return 0;  // 对端关闭
            if(errno == EINTR) continue;    // 被信号打断
            else if(errno == EAGAIN || errno == EWOULDBLOCK) return -2;//暂无数据
            else { ERROR_LOG("recv failed: %s", strerror(errno)); return -1;} //其他报错
        }
    }
    ssize_t NonBlockRecv(void *buf, size_t len){
        return Recv(buf, len, MSG_DONTWAIT);
    }
};


class Channel{
private:
    int _fd;
    uint32_t _events;
    uint32_t _revents;
    using EventCallback = std::function<void()>;

};